/*
 * Copyright 2022 The Open Islands Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openislands.oi.console.utils;

import org.openislands.oi.console.exception.InvokeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

public class ClassScanUtils {
    private static final Logger log = LoggerFactory.getLogger(ClassScanUtils.class);

    /***
     * Scanner all class from packages path under
     * @param packages package list
     * @return class set
     */
    public static Set<Class<?>> scan(String... packages) {
        Set<Class<?>> classes = new HashSet<>();
        for (String aPackage : packages) {
            try {
                String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                        ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(aPackage)) + "/**/*.class";

                PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
                CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(patternResolver);
                Resource[] resources = patternResolver.getResources(packageSearchPath);

                for (Resource resource : resources) {
                    if (resource.isReadable()) {
                        try {
                            MetadataReader metadataReader = readerFactory.getMetadataReader(resource);
                            classes.add(loadClass(metadataReader.getClassMetadata().getClassName()));
                        } catch (Exception e) {
                            log.warn("load class failed: " + e.getMessage());
                        }
                    }
                }
            } catch (IOException e) {
                throw new InvokeException("load class resources failed.");
            }
        }

        return classes;
    }

    /***
     * Load class and does not trigger static code blocks
     * @param packagePath class name
     * @return class
     * @throws ClassNotFoundException not found in class path
     */
    public static Class<?> loadClass(String packagePath) throws ClassNotFoundException {
        return Thread.currentThread().getContextClassLoader().loadClass(packagePath);
    }
}
